<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
<script>
    // 遍历方法
    // 特别注意 Map的遍历顺序 就是插入顺序

    // Map.prototype.keys() 返回键名的遍历器
    // Map.prototype.values() 返回键值的遍历器
    // Map.prototype.entries() 返回所有成员的遍历器
    // Map.prototype.forEach() 遍历Map的所有成员
    const map = new Map([
        ['F', 'no'],
        ['T',  'yes'],
    ]);

    for (let key of map.keys()) {
        console.log(key);
    }
    // "F"
    // "T"

    for (let value of map.values()) {
        console.log(value);
    }
    // "no"
    // "yes"

    for (let item of map.entries()) {
        console.log(item[0], item[1]);
    }
    // "F" "no"
    // "T" "yes"

    // 或者
    for (let [key, value] of map.entries()) {
        console.log(key, value);
    }
    // "F" "no"
    // "T" "yes"

    // 等同于使用map.entries()
    for (let [key, value] of map) {
        console.log(key, value);
    }
    // "F" "no"
    // "T" "yes"

    // 最后一例 表示Map结构的默认遍历器接口(Symbol.iterator属性) 就是entries方法
    map[Symbol.iterator] === map.entries
    // true


    // Map结构转为数组结构 比较快速的方法是使用扩展运算符
    const map = new Map([
        [1, 'one'],
        [2, 'two'],
        [3, 'three'],
    ]);

    // [...map.keys()]
    // [1, 2, 3]

    // [...map.values()]
    // ['one', 'two', 'three']

    // [...map.entries()]
    // [[1,'one'], [2, 'two'], [3, 'three']]

    // [...map]
    // [[1,'one'], [2, 'two'], [3, 'three']]



    // 结合数组的map和filter方法可以实现Map的遍历和过滤
    // Map本身没有map和filter方法
    const map0 = new Map()
    .set(1, 'a')
    .set(2, 'b')
    .set(3, 'c');

    const map1 = new Map(
        [...map0].filter(([k, v]) => k < 3)
    );
    // 产生 Map 结构 {1 => 'a', 2 => 'b'}

    const map2 = new Map(
        [...map0].map(([k, v]) => [k * 2, '_' + v])
    );
    // 产生 Map 结构 {2 => '_a', 4 => '_b', 6 => '_c'}


    // 此外 Map 还有一个forEach方法 与数组的forEach方法类似 也可以实现遍历
    map.forEach(function(value, key, map) {
        console.log("Key: %s, Value: %s", key, value);
    });

    // Map 的forEach方法还可以接受第二个参数 用来绑定this
    const reporter = {
        report: function(key, value) {
            console.log("Key: %s, Value: %s", key, value);
        }
    };
    // forEach方法的回调函数的this，就指向reporter。
    map.forEach(function(value, key, map) {
        this.report(key, value);
    }, reporter);



    // 与其他数据结构的互相转换
    // Map转数组
    const myMap = new Map()
    .set(true, 7)
    .set({foo: 3}, ['abc']);
    [...myMap]
    // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

    // 数组转Map
    new Map([
        [true, 7],
        [{foo: 3}, ['abc']]
    ])
    // Map {
    //   true => 7,
    //   Object {foo: 3} => ['abc']
    // }

    // Map转对象
    // 如果所有Map的键都是字符串 它可以无损地转为对象
    function strMapToObj(strMap) {
        let obj = Object.create(null);
        for (let [k,v] of strMap) {
            obj[k] = v;
        }
        return obj;
    }

    const myMap = new Map()
        .set('yes', true)
        .set('no', false);
    strMapToObj(myMap)
    // { yes: true, no: false }
    // 如果有非字符串的键名 那么这个键名会被转成字符串 再作为对象的键名

    // 对象转Map
    function objToStrMap(obj) {
    let strMap = new Map();
        for (let k of Object.keys(obj)) {
            strMap.set(k, obj[k]);
        }
        return strMap;
    }

    objToStrMap({yes: true, no: false})
    // Map {"yes" => true, "no" => false}

    // Map转JSON
    // 分两种情况 一、Map的键名都是字符串 可以选择转为对象JSON
    function strMapToJson(strMap) {
        return JSON.stringify(strMapToObj(strMap));
    }

    let myMap = new Map().set('yes', true).set('no', false);
    strMapToJson(myMap)
    // '{"yes":true,"no":false}'

    // 二、Map的键名有非字符串 可以选择转为数组JSON
    function mapToArrayJson(map) {
        return JSON.stringify([...map]);
    }

    let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
    mapToArrayJson(myMap)
    // '[[true,7],[{"foo":3},["abc"]]]'

    // JSON转Map
    // 正常情况下 所有键名都是字符串
    function jsonToStrMap(jsonStr) {
        return objToStrMap(JSON.parse(jsonStr));
    }

    jsonToStrMap('{"yes": true, "no": false}')
    // Map {'yes' => true, 'no' => false}



    // 特殊情况 整个JSON是数组 每个数组成员本身又是一个有两个成员的数组 
    // 这时 它可以一一对应地转为Map 这往往是Map转为数组JSON的逆操作
    function jsonToMap(jsonStr) {
        return new Map(JSON.parse(jsonStr));
    }

    jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
    // Map {true => 7, Object {foo: 3} => ['abc']}


    


</script>